﻿using System;
using System.ComponentModel.Composition;
using Microsoft.SilverlightMediaFramework.Plugins.Metadata;
using Microsoft.SilverlightMediaFramework.Plugins.Primitives;
using Microsoft.SilverlightMediaFramework.Plugins.Heuristics;

namespace Microsoft.SilverlightMediaFramework.Plugins.DualMedia
{
    [ExportMediaPlugin(PluginName = PluginName,
        PluginDescription = PluginDescription,
        PluginVersion = PluginVersion,
        SupportedDeliveryMethods = SupportedDeliveryMethodsInternal)]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class DualAdaptiveMediaPlugin : DualMediaPlugin, IAdaptiveMediaPlugin
    {
        private const string PluginName = "DualAdaptiveMediaPlugin";
        private const string PluginDescription = "Provides 3D capabilities for the Silverlight Media Framework using multiple AdaptiveMediaPlugins.";
        private const string PluginVersion = "2.2011.0410.1";
        private const DeliveryMethods SupportedDeliveryMethodsInternal = DeliveryMethods.Streaming | DeliveryMethods.AdaptiveStreaming;

        private IHeuristicsPlugin heuristics;

        public event Func<Uri, Uri> GetLeftAdaptiveSource;
        public event Func<Uri, Uri> GetRightAdaptiveSource;

        public Uri AdaptiveSource
        {
            get
            {
                return LeftAdaptiveSource.AdaptiveSource;
            }
            set
            {
                LeftAdaptiveSource.AdaptiveSource = (GetLeftAdaptiveSource != null) ? GetLeftAdaptiveSource(value) : value;
                RightAdaptiveSource.AdaptiveSource = (GetRightAdaptiveSource != null) ? GetRightAdaptiveSource(value) : value;
            }
        }

        public Primitives.ChunkDownloadStrategy ChunkDownloadStrategy
        {
            get
            {
                return LeftAdaptiveSource.ChunkDownloadStrategy;
            }
            set
            {
                LeftAdaptiveSource.ChunkDownloadStrategy = value;
                RightAdaptiveSource.ChunkDownloadStrategy = value;
            }
        }

        public bool IsSourceAdaptive
        {
            get { return LeftAdaptiveSource.IsSourceAdaptive; }
        }

        public long MaximumPossibleBitrate
        {
            get { return LeftAdaptiveSource.MaximumPossibleBitrate; }
        }

        public System.Collections.Generic.IEnumerable<Primitives.ISegment> Segments
        {
            get { return LeftAdaptiveSource.Segments; }
        }

        public Primitives.ISegment CurrentSegment
        {
            get { return LeftAdaptiveSource.CurrentSegment; }
        }

        public Primitives.IMediaTrack VideoDownloadTrack
        {
            get { return LeftAdaptiveSource.VideoDownloadTrack; }
        }

        public Primitives.IMediaTrack MaximumPlayableVideoTrack
        {
            get { return LeftAdaptiveSource.MaximumPlayableVideoTrack; }
        }

        public Primitives.IMediaTrack VideoPlaybackTrack
        {
            get { return LeftAdaptiveSource.VideoPlaybackTrack; }
        }

        public System.Collections.Generic.IDictionary<string, string> ManifestAttributes
        {
            get { return LeftAdaptiveSource.ManifestAttributes; }
        }

        public event Action<IAdaptiveMediaPlugin, Primitives.IMediaTrack> VideoPlaybackTrackChanged;

        public event Action<IAdaptiveMediaPlugin, Primitives.IMediaTrack> VideoDownloadTrackChanged;

        public event Action<IAdaptiveMediaPlugin, Exception> AdaptiveStreamingErrorOccurred;

        public event Action<IAdaptiveMediaPlugin> ManifestReady;

        public event Action<IAdaptiveMediaPlugin, Primitives.IMediaStream> StreamSelected;

        public event Action<IAdaptiveMediaPlugin, Primitives.IMediaStream> StreamUnselected;

        public event Action<IAdaptiveMediaPlugin, System.Collections.Generic.IEnumerable<Primitives.IMediaStream>, Exception> StreamSelectionFailed;

        public event Action<IAdaptiveMediaPlugin, Primitives.IMediaStream, Primitives.IDataChunk> StreamDataAdded;

        public event Action<IAdaptiveMediaPlugin, Primitives.IMediaStream, TimeSpan> StreamDataRemoved;

        public event Action<IAdaptiveMediaPlugin, Primitives.IMediaTrack, Primitives.IDataChunk, Exception> DownloadStreamDataFailed;

        public event Action<IAdaptiveMediaPlugin, Primitives.IMediaTrack, Primitives.IStreamDownloadResult> DownloadStreamDataCompleted;

        protected override void AttachEvents()
        {
            base.AttachEvents();

            LeftAdaptiveSource.VideoPlaybackTrackChanged += new Action<IAdaptiveMediaPlugin, IMediaTrack>(LeftAdaptiveSource_VideoPlaybackTrackChanged);
            LeftAdaptiveSource.VideoDownloadTrackChanged += new Action<IAdaptiveMediaPlugin, IMediaTrack>(LeftAdaptiveSource_VideoDownloadTrackChanged);
            LeftAdaptiveSource.AdaptiveStreamingErrorOccurred += new Action<IAdaptiveMediaPlugin, Exception>(LeftAdaptiveSource_AdaptiveStreamingErrorOccurred);
            LeftAdaptiveSource.ManifestReady += new Action<IAdaptiveMediaPlugin>(LeftAdaptiveSource_ManifestReady);
            LeftAdaptiveSource.StreamSelected += new Action<IAdaptiveMediaPlugin, IMediaStream>(LeftAdaptiveSource_StreamSelected);
            LeftAdaptiveSource.StreamUnselected += new Action<IAdaptiveMediaPlugin, IMediaStream>(LeftAdaptiveSource_StreamUnselected);
            LeftAdaptiveSource.StreamSelectionFailed += new Action<IAdaptiveMediaPlugin, System.Collections.Generic.IEnumerable<IMediaStream>, Exception>(LeftAdaptiveSource_StreamSelectionFailed);
            LeftAdaptiveSource.StreamDataAdded += new Action<IAdaptiveMediaPlugin, IMediaStream, IDataChunk>(LeftAdaptiveSource_StreamDataAdded);
            LeftAdaptiveSource.StreamDataRemoved += new Action<IAdaptiveMediaPlugin, IMediaStream, TimeSpan>(LeftAdaptiveSource_StreamDataRemoved);
            LeftAdaptiveSource.DownloadStreamDataFailed += new Action<IAdaptiveMediaPlugin, IMediaTrack, IDataChunk, Exception>(LeftAdaptiveSource_DownloadStreamDataFailed);
            LeftAdaptiveSource.DownloadStreamDataCompleted += new Action<IAdaptiveMediaPlugin, IMediaTrack, IStreamDownloadResult>(LeftAdaptiveSource_DownloadStreamDataCompleted);
        }

        protected override void DetachEvents()
        {
            base.DetachEvents();

            LeftAdaptiveSource.VideoPlaybackTrackChanged -= new Action<IAdaptiveMediaPlugin, IMediaTrack>(LeftAdaptiveSource_VideoPlaybackTrackChanged);
            LeftAdaptiveSource.VideoDownloadTrackChanged -= new Action<IAdaptiveMediaPlugin, IMediaTrack>(LeftAdaptiveSource_VideoDownloadTrackChanged);
            LeftAdaptiveSource.AdaptiveStreamingErrorOccurred -= new Action<IAdaptiveMediaPlugin, Exception>(LeftAdaptiveSource_AdaptiveStreamingErrorOccurred);
            LeftAdaptiveSource.ManifestReady -= new Action<IAdaptiveMediaPlugin>(LeftAdaptiveSource_ManifestReady);
            LeftAdaptiveSource.StreamSelected -= new Action<IAdaptiveMediaPlugin, IMediaStream>(LeftAdaptiveSource_StreamSelected);
            LeftAdaptiveSource.StreamUnselected -= new Action<IAdaptiveMediaPlugin, IMediaStream>(LeftAdaptiveSource_StreamUnselected);
            LeftAdaptiveSource.StreamSelectionFailed -= new Action<IAdaptiveMediaPlugin, System.Collections.Generic.IEnumerable<IMediaStream>, Exception>(LeftAdaptiveSource_StreamSelectionFailed);
            LeftAdaptiveSource.StreamDataAdded -= new Action<IAdaptiveMediaPlugin, IMediaStream, IDataChunk>(LeftAdaptiveSource_StreamDataAdded);
            LeftAdaptiveSource.StreamDataRemoved -= new Action<IAdaptiveMediaPlugin, IMediaStream, TimeSpan>(LeftAdaptiveSource_StreamDataRemoved);
            LeftAdaptiveSource.DownloadStreamDataFailed -= new Action<IAdaptiveMediaPlugin, IMediaTrack, IDataChunk, Exception>(LeftAdaptiveSource_DownloadStreamDataFailed);
            LeftAdaptiveSource.DownloadStreamDataCompleted -= new Action<IAdaptiveMediaPlugin, IMediaTrack, IStreamDownloadResult>(LeftAdaptiveSource_DownloadStreamDataCompleted);
        }

        void LeftAdaptiveSource_DownloadStreamDataCompleted(IAdaptiveMediaPlugin arg1, IMediaTrack arg2, IStreamDownloadResult arg3)
        {
            if (DownloadStreamDataCompleted != null) DownloadStreamDataCompleted(this, arg2, arg3);
        }

        void LeftAdaptiveSource_DownloadStreamDataFailed(IAdaptiveMediaPlugin arg1, IMediaTrack arg2, IDataChunk arg3, Exception arg4)
        {
            if (DownloadStreamDataFailed != null) DownloadStreamDataFailed(this, arg2, arg3, arg4);
        }

        void LeftAdaptiveSource_StreamDataRemoved(IAdaptiveMediaPlugin arg1, IMediaStream arg2, TimeSpan arg3)
        {
            if (StreamDataRemoved != null) StreamDataRemoved(this, arg2, arg3);
        }

        void LeftAdaptiveSource_StreamDataAdded(IAdaptiveMediaPlugin arg1, IMediaStream arg2, IDataChunk arg3)
        {
            if (StreamDataAdded != null) StreamDataAdded(this, arg2, arg3);
        }

        void LeftAdaptiveSource_StreamSelectionFailed(IAdaptiveMediaPlugin arg1, System.Collections.Generic.IEnumerable<IMediaStream> arg2, Exception arg3)
        {
            if (StreamSelectionFailed != null) StreamSelectionFailed(this, arg2, arg3);
        }

        void LeftAdaptiveSource_StreamUnselected(IAdaptiveMediaPlugin arg1, IMediaStream arg2)
        {
            if (StreamUnselected != null) StreamUnselected(this, arg2);
        }

        void LeftAdaptiveSource_StreamSelected(IAdaptiveMediaPlugin arg1, IMediaStream arg2)
        {
            if (StreamSelected != null) StreamSelected(this, arg2);
        }

        void LeftAdaptiveSource_ManifestReady(IAdaptiveMediaPlugin obj)
        {
            if (ManifestReady != null) ManifestReady(this);
        }

        void LeftAdaptiveSource_AdaptiveStreamingErrorOccurred(IAdaptiveMediaPlugin arg1, Exception arg2)
        {
            if (AdaptiveStreamingErrorOccurred != null) AdaptiveStreamingErrorOccurred(this, arg2);
        }

        void LeftAdaptiveSource_VideoDownloadTrackChanged(IAdaptiveMediaPlugin arg1, IMediaTrack arg2)
        {
            if (VideoDownloadTrackChanged != null) VideoDownloadTrackChanged(this, arg2);
        }

        void LeftAdaptiveSource_VideoPlaybackTrackChanged(IAdaptiveMediaPlugin arg1, IMediaTrack arg2)
        {
            if (VideoPlaybackTrackChanged != null) VideoPlaybackTrackChanged(this, arg2);
        }

        public void ClearBuffer(TimeSpan timeSpan)
        {
            LeftAdaptiveSource.ClearBuffer(timeSpan);
            RightAdaptiveSource.ClearBuffer(timeSpan);
        }

        public void DownloadStreamData(Primitives.IMediaTrack track)
        {
            LeftAdaptiveSource.DownloadStreamData(track);
            RightAdaptiveSource.DownloadStreamData(track);
        }

        public void DownloadStreamData(Primitives.IMediaTrack track, Primitives.IDataChunk chunk)
        {
            LeftAdaptiveSource.DownloadStreamData(track, chunk);
            RightAdaptiveSource.DownloadStreamData(track, chunk);
        }

        public void SetVideoBitrateRange(long minimumVideoBitrate, long maximumVideoBitrate, bool flushBuffer = true)
        {
            LeftAdaptiveSource.SetVideoBitrateRange(minimumVideoBitrate, maximumVideoBitrate, flushBuffer);
            RightAdaptiveSource.SetVideoBitrateRange(minimumVideoBitrate, maximumVideoBitrate, flushBuffer);
        }

        public void SetCacheProvider(object cacheProvider)
        {
            LeftAdaptiveSource.SetCacheProvider(cacheProvider);
            RightAdaptiveSource.SetCacheProvider(cacheProvider);
        }

        public void SetSegmentSelectedStreams(Primitives.ISegment segment, System.Collections.Generic.IEnumerable<Primitives.IMediaStream> streams)
        {
            LeftAdaptiveSource.SetSegmentSelectedStreams(segment, streams);
            RightAdaptiveSource.SetSegmentSelectedStreams(segment, streams);
        }

        public void ModifySegmentSelectedStreams(Primitives.ISegment segment, System.Collections.Generic.IEnumerable<Primitives.IMediaStream> streamsToAdd, System.Collections.Generic.IEnumerable<Primitives.IMediaStream> streamsToRemove)
        {
            LeftAdaptiveSource.ModifySegmentSelectedStreams(segment, streamsToAdd, streamsToRemove);
            RightAdaptiveSource.ModifySegmentSelectedStreams(segment, streamsToAdd, streamsToRemove);
        }

        public IAdaptiveMediaPlugin LeftAdaptiveSource
        {
            get { return LeftSource as IAdaptiveMediaPlugin; }
            set { LeftSource = value; }
        }

        public IAdaptiveMediaPlugin RightAdaptiveSource
        {
            get { return RightSource as IAdaptiveMediaPlugin; }
            set { RightSource = value; }
        }

        public override void Load()
        {
            base.Load();

            heuristics = new PrioritizedHeuristicsPlugin();
            heuristics.Load();
            heuristics.RegisterPlugin(LeftAdaptiveSource, 0, false);
            heuristics.RegisterPlugin(RightAdaptiveSource, 1, true);
        }

        public override void Unload()
        {
            heuristics.UnregisterPlugin(LeftAdaptiveSource);
            heuristics.UnregisterPlugin(RightAdaptiveSource);
            heuristics.Unload();
            heuristics = null;

            base.Unload();
        }
    }
}
